﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
using TrainGame.Model;
using TrainGame.Engine;
using TrainGame.Engine.RouteCalc;

namespace TrainGame.AI.TileLay
{
	/// <summary>
	/// "default" tile lay goal - mainly a placeholder for the old multi-goal approach.  Eventually will be deprecated.
	/// </summary>
	[Obsolete ("going away, don't use any more")]
	internal class OldTileLayAiTileGoal : TileLayGoal
	{
		public override string GetName(PublicCompany company)
		{
			return "OldTileLayAI";
		}

		protected override int ComputeNeed(Gameboard gameboard, PublicCompany company, Dictionary<string, GameboardHex> runToHexes, AiMoveConfig config)
		{
			return 1;	// anything non-zero is fine for now, but low enough that any 'real' goal should override it
		}

		internal override List<AiTileLay> ComputeTileLays(Stopwatch watch, TimeSpan timeLimit, Gameboard gameboard, PublicCompany company, List<AiTileLay> tileLays, Dictionary<string, GameboardHex> runToHexes, Dictionary<string, GameboardHex> tileLayHexes, AiMoveConfig config)
		{
			Dictionary<string, int> SpecialValueHexes = ComputeSpecialValueHexes(tileLayHexes, company);

			return ComputeBestTileLay(gameboard, tileLays, runToHexes, SpecialValueHexes, config, company,
				watch, timeLimit);
		}

		static private Dictionary<string, int> ComputeSpecialValueHexes(Dictionary<string, GameboardHex> tileLayHexes, PublicCompany company)
		{
			Dictionary<string, int> SpecialValueHexes = new Dictionary<string, int>();

			Player player = company.Owner;
			Game game = company.Game;

			foreach (GameboardHex hex in tileLayHexes.Values)
			{
				int value = 0;
				// if this hex is the destination hex for any of the player's companies, give it a value
				foreach (PublicCompany co in game.PublicCompanies)
				{
					if (co.NeedDestination && (co.Destination == hex.Location) && (co.Owner == player))
						value += 20;
				}

				if (value > 0)
					SpecialValueHexes.Add(hex.Location, value);
			}

			return SpecialValueHexes;
		}

		static private List<AiTileLay> ComputeBestTileLay(Gameboard origGameboard, List<AiTileLay> tileLays,
			Dictionary<string, GameboardHex> runToHexes, Dictionary<string, int> SpecialValueHexes,
			AiMoveConfig config, PublicCompany company,
			Stopwatch watch, TimeSpan timeLimit)
		{
			List<AiTileLay> bestTileLays = null;
			int bestValue = -1;
			int bestGoalValue = 0;
			// make copy of game board tiles and hexes, so we can modify it without affecting the real game board
			Gameboard gameboard = origGameboard.MakeCopy();

			foreach (AiTileLay tileLay in tileLays)
			{
				if (watch.Elapsed.CompareTo(timeLimit) > 0)
					goto done;	// time limit expired, return the best tile lay so far

				gameboard.ApplyTileLayWithUndo(tileLay);	// add tile lay to game board
				ComputeTileGoalValue(tileLay, gameboard, company, runToHexes, SpecialValueHexes);

				RouteCollection routes = RouteCalculator.FindBestRoutes(gameboard, company, company.Trains, false);

				gameboard.UndoTileLay(tileLay);	// restore the game board

				if (routes.TotalValue + tileLay.GoalValue == bestValue + bestGoalValue)
					bestTileLays.Add(tileLay);
				else if (routes.TotalValue + tileLay.GoalValue > bestValue + bestGoalValue)
				{
					bestTileLays = new List<AiTileLay>();
					bestTileLays.Add(tileLay);
					bestValue = routes.TotalValue;
					bestGoalValue = tileLay.GoalValue;
				}
			}

		done:
			return bestTileLays;
		}

		static private void ComputeTileGoalValue(AiTileLay tileLay, Gameboard gameboard, PublicCompany company,
			Dictionary<string, GameboardHex> runToHexes, Dictionary<string, int> SpecialValueHexes)
		{
			int baseValue = 0;
			int bestValue = 0;

			if (!runToHexes.Keys.Contains<string>(tileLay.Hex.Location))
			{
				// this tile lay connected to a hex the company couldn't reach before,
				// see if it's a city the company couldn't reach before
				if (tileLay.Hex.TownType != GameboardHex.eTownType.None)
					baseValue += tileLay.Hex.Value / 2;

				// check for any special hex value
				if (SpecialValueHexes.Keys.Contains(tileLay.Hex.Location))
					baseValue += SpecialValueHexes[tileLay.Hex.Location];
			}
			else
			{
				// it's an upgrade/downgrade tile
				// if it's a city, value the city upgrade (independent of whether our best route increased)
				if (tileLay.Hex.TownType != GameboardHex.eTownType.None)
					baseValue += 4;//9;// tileLay.Hex.Value / 2;
			}

			// figure out the new routes through this hex
			// TODO: only check routes this compnay can reach (otherwise algortihm will value double towns too highly)
			//		 (And remove HACK below when fixing this)
			foreach (Side side in tileLay.Hex.NewRoutes.Keys)
			{
				if (CanRunIntoTileFromSide(tileLay.Hex, side, company, gameboard))
				{
					SideCollection destSides = (SideCollection)tileLay.Hex.NewRoutes[side];
					foreach (Side dest in destSides)
					{
#if no
						// Joe: this never happens, they've always been converted to compass directions by this point
						Side realSide;
						if (dest.LetterOrCompass == Side.LetterOrCompassEnum.Letter)
						{
							realSide = new Side();
							realSide.ConvertToCompassWithRotation(dest, tileLay.Rotation);
						}
						else
							realSide = dest;
						string direction = realSide.GetDirection();
#endif
						string direction = dest.GetDirection();

						if (!tileLay.NewRoutes.Contains(direction))
						{
							bool found = false;
							foreach (Side startSide in tileLay.Hex.ExistingRoutes.Keys)
							{
								if (startSide.GetDirection() == direction)
									found = true;
							}
							if (!found)
								tileLay.NewRoutes.Add(direction);
						}
					}
				}
			}

			foreach (string direction in tileLay.NewRoutes)
			{
				int value = 0;
				bool lookAtNext = false;

				// get the tile we connected to, or if no tile, the next hex in that direction
				string sdestHex = (string)tileLay.Hex.Neighbors[direction];
				GameboardHex destHex = gameboard.GameboardHexes[sdestHex];
				if (destHex != null)
				{
					// the tile lay aim at a hex the company couldn't reach before
					if (!runToHexes.Keys.Contains<string>(destHex.Location))
					{
						lookAtNext = (destHex.Tile == null) && (destHex.TownType != GameboardHex.eTownType.None);
						value += 1;

						// check for any special hex value
						if (SpecialValueHexes.Keys.Contains(sdestHex))
							baseValue += SpecialValueHexes[sdestHex];

						// the tile lay aimed at a city the company couldn't reach before
						if ((destHex.TownType == GameboardHex.eTownType.City) ||
							(destHex.HexType == GameboardHex.eHexType.OffBoard))
						{
							value += 2;//4;
							lookAtNext = false;
						}
						// the tile lay aimed at a town the company couldn't reach before
						else if ((destHex.TownType == GameboardHex.eTownType.Town) ||
							(destHex.TownType == GameboardHex.eTownType.DblTown))
						{
							value += 1;
							lookAtNext = false;
						}
					}

					if (lookAtNext)
					{
						// TODO: this is just looking in same direction, but ideally we'd check all directions
						string saimHex = (string)destHex.Neighbors[direction];//.Compass];
						if (saimHex != null)
						{
							GameboardHex aimHex = gameboard.GameboardHexes[saimHex];
							// the tile lay aims AT a hex the company couldn't reach before
							if ((aimHex != null) && (!runToHexes.Keys.Contains<string>(aimHex.Location)))
							{
								value += 1;

								// the tile lay aims AT a city the company couldn't reach before
								if ((aimHex.TownType == GameboardHex.eTownType.City) ||
									(aimHex.HexType == GameboardHex.eHexType.OffBoard))
									value += 1;//2;
								// the tile lay aims AT a town the company couldn't reach before
								else if ((aimHex.TownType == GameboardHex.eTownType.Town) ||
									(aimHex.TownType == GameboardHex.eTownType.DblTown))
									value += 0;//1;
							}
						}
					}
				}

				// save the highest value for this hex (think about summing them instead)
				//if (value > bestValue)
				//	bestValue = value;

				// sum all the values for this hex
				bestValue += value;
			}

			// HACK: because algorithm values double town tiles too highly, account for that
			if (tileLay.Tile.TownType == GameboardHex.eTownType.DblTown)
			{
				bestValue /= 3;// 2;
				// and those with no upgrade/downgrade path are especially bad
				if ((tileLay.Tile.ValidUpgrades.Count == 0) && (tileLay.Tile.ValidDowngrades.Count == 0))
					bestValue /= 2;
			}

			tileLay.GoalValue = bestValue + baseValue;
		}

		/// <summary>
		/// Can the company run to the 'side' hexside of the tile in this hex?
		/// </summary>
		/// <remarks>
		/// The hex and tile lay should have been validated before calling this method.
		/// We're concerned here with multi-route tiles (green and brown track tiles, double towns and separated-city tiles),
		/// where the company can only run on particular segments of track and not others.
		/// </remarks>
		/// <param name="hex"></param>
		/// <param name="side"></param>
		/// <param name="company"></param>
		/// <param name="gameboard"></param>
		/// <returns></returns>
		static private bool CanRunIntoTileFromSide(GameboardHex hex, Side side, PublicCompany company, Gameboard gameboard)
		{
			return true;	//TODO: fix this!
		}
	}
}
